﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using iTunesLib;

namespace iTunesPlaycountUpdater
{
    class Program
    {
        static iTunesApp iTunes;
        static string lastFmApiKey = @"49f694be0c92a2719c51ef324c57a60d";

        static void Main(string[] args)
        {
            Console.Title = "iTunes Play Count Updater";

            StartItunes();
            string userName = GetLastFmUserName();

            long ticksBefore = DateTime.Now.Ticks;
            Console.WriteLine("Indexing Last.Fm tracks (this can take a while)...");
            var tracks = GetLastFmTracks(lastFmApiKey, userName);

            IITTrackCollection iTunesTracks = iTunes.LibraryPlaylist.Tracks;
            int count = iTunesTracks.Count;
            List<TrackReport> reports = new List<TrackReport>();

            Parallel.For(1, count, i =>
            {
                Console.Clear();
                Console.WriteLine("Processing tracks...");

                IITTrack iTunesTrack = iTunesTracks[i];
                LastFmTrack lastFmTrack;

                if (iTunesTrack != null)
                {
                    lastFmTrack = GetTrack(iTunesTrack.Artist, iTunesTrack.Album, iTunesTrack.Name, tracks);
                    if (lastFmTrack != null)
                    {
                        //iTunesTrack.PlayedCount--;
                        if (iTunesTrack.PlayedCount < lastFmTrack.Plays)
                        {
                            reports.Add(new TrackReport(lastFmTrack.Name, lastFmTrack.Artist, lastFmTrack.Album, iTunesTrack.PlayedCount, lastFmTrack.Plays));

                            iTunesTrack.PlayedCount = lastFmTrack.Plays;
                        }
                    }
                }
            });

            GenerateReport(reports, TimeSpan.FromTicks(DateTime.Now.Ticks - ticksBefore));

        }

        static void StartItunes()
        {
            while (iTunes == null)
            {
                Console.WriteLine("Starting iTunes...");
                iTunes = new iTunesApp();
                Console.Clear();
            }

        }

        static string GetLastFmUserName()
        {
            Console.Clear();
            Console.WriteLine("Please enter your Last.Fm username...");
            return Console.ReadLine();
        }
        static IEnumerable<LastFmTrack> GetLastFmTracks(string apiKey, string userName)
        {
            IEnumerable<LastFmTrack> tracks = new LastFmEnumerable(apiKey, userName);

            return tracks.ToArray();
        }
        static LastFmTrack GetTrack(string artist, string album, string name, IEnumerable<LastFmTrack> tracks)
        {
            return (from t in tracks
                    where t.Artist == artist
                    where t.Album == album
                    where t.Name == name
                    select t).FirstOrDefault();
        }
        static void GenerateReport(List<TrackReport> trackReports, TimeSpan duration)
        {
            Console.Clear();
            Console.Write("Generate report (Y/N)? ");
            if (Console.ReadLine() != "Y")
                return;
            Console.Write("Create [X]ml based or [T]ext based report? ");
            bool createXml = Console.ReadLine() == "X";
            var create = createXml ? new CreateReport(CreateXmlReport) : new CreateReport(CreateTextReport);
            Console.WriteLine("Creating report...");

            string location = Path.Combine(
                Path.GetTempPath(),
                string.Format(@"iTunes Playcount Updater Report {0}.{1}.{2}",
                    DateTime.Now.ToShortDateString(),
                    Path.GetRandomFileName(),
                    createXml ? ".xml" : ".txt"));

            var tracks = from t in trackReports
                         group t by t.Artist into artists
                         from artist in
                             (from track in artists
                              group track by track.Album)
                         group artist by artists.Key;

            File.WriteAllText(location, create(tracks, duration, trackReports.Count));

            Console.Clear();
            Console.Write("Report saved in {0}.\nOpen now? ", location);
            if (Console.ReadLine() == "Y")
                System.Diagnostics.Process.Start(location);
        }

        delegate string CreateReport(IEnumerable<IGrouping<string, IGrouping<string, TrackReport>>> reports, TimeSpan duration, int count);
        static string CreateTextReport(IEnumerable<IGrouping<string, IGrouping<string, TrackReport>>> reports, TimeSpan duration, int count)
        {

            StringBuilder builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                writer.WriteLine("{0} Tracks were updated.", count);
                writer.WriteLine("Total running time: {0}m {1}s {2}ms", duration.Minutes, duration.Seconds, duration.Milliseconds);
                writer.WriteLine();

                foreach (var artist in reports)
                {
                    writer.WriteLine("{0}", artist.Key);
                    foreach (var album in artist)
                    {
                        writer.WriteLine("  {0}", album.Key);
                        writer.WriteLine();
                        foreach (var track in album)
                        {
                            writer.WriteLine("    {0}", track.Name);
                            writer.WriteLine("    Changed playcount from {0} to {1}", track.OldPlayCount, track.NewPlayCount);
                            writer.WriteLine();
                        }
                    }
                }
            }
            return builder.ToString();
        }
        static string CreateXmlReport(IEnumerable<IGrouping<string, IGrouping<string, TrackReport>>> reports, TimeSpan duration, int count)
        {
            XDocument document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
            XElement root =
                new XElement("updatedtracksreport",
                    new XAttribute("runningtimeinticks", duration.Ticks),
                    new XAttribute("updatedtrackscount", count));

            foreach (var artist in reports)
            {
                XElement artistElement =
                    new XElement("artist",
                        new XAttribute("name", artist.Key));
                foreach (var album in artist)
                {
                    XElement albumElement =
                        new XElement("album",
                            new XAttribute("name", album.Key));
                    foreach (var track in album)
                    {
                        albumElement.Add(
                            new XElement("track",
                                new XAttribute("name", track.Name),
                                new XElement("oldplaycount", track.OldPlayCount),
                                new XElement("newplaycount", track.NewPlayCount)));
                    }
                    artistElement.Add(albumElement);
                }
                root.Add(artistElement);
            }
            document.Add(root);

            var builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                document.Save(writer);
            }
            return builder.ToString();
        }

    }
}
